#include "Shell.h"
#include <boost/assert.hpp>

namespace boost
{
	void assertion_failed(char const * expr, char const * function, char const * file, long line)
	{
		throw SException("Boost exception.");
	}
}


Shell::Shell( INameResolutorSp nameResolutor )
{
	this->scott = ScottSp( new Scott() );
	this->nameResolutor = nameResolutor;

	this->plugins = PluginSpMapSp( new PluginSpMap() );
	this->vars = SValueSpMapSp( new SValueSpMap() );

	// Default settings if you don't use configuration file
	autovars = true;
	implicitvars = true;
	dumpvars = true;

	// Get the reference to the Descriptor Repository
	descriptorsRepository = DescriptorsRepository::GetInstance();
}

ShellSp Shell::Create(INameResolutorSp nameResolutor)
{
	return ShellSp(new Shell(nameResolutor));
}

#if !defined(WIN32)

static char *line_read = (char *)NULL;

char* readline_gets(const char* prompt)
{
  /* If the buffer has already been allocated,
     return the memory to the free pool. */
    if (line_read)
    {
       free (line_read);
       line_read = (char *)NULL;
    }
    
    /* Get a line from the user. */
    line_read = readline (prompt);

    /* If the line has any text in it, save it on the history. */
    if (line_read && *line_read)
    {
	add_history (line_read);
    }

    return (line_read);
}

#endif

string Shell::ReadLine()
{
	string input;
	bool readingSettings;
	bool commentFound;

	do 
	{
		readingSettings = !inputLines.empty();
		commentFound = false;

		if ( !readingSettings )
		{
			#if defined(WIN32)
				cout << "scottsh-1.0>";
				if ( !getline(cin, input) ) 
				{
					cin.clear();
				}
			#else			
				readline_gets("scottsh-1.0>");
				if ( line_read )
				{
					input = string(line_read);
				}
			#endif		

		}
		else
		{
			input = inputLines.front();
			inputLines.pop();
		}
		
		trim(input);

		// Ignore empty line and comment starting with #
		if ( input.empty() || input.at(0) == '#')
		{
			commentFound = true;
		}

		if ( readingSettings && !commentFound  )
		{
			cout << "scottsh-1.0>" << input << endl;
		}

	} while ( commentFound );

	return input;
}

string Shell::ReadAllFile( ifstream &file )
{
	ostringstream os;

	char c;
	while (file.good())
	{
		c = file.get();      
		if ( file.good() )
		{
			os << c;
		}
	}

	file.close();

	return os.str();
}

void Shell::Start()
{
	try
	{	
		// Read the scott setting
		string scottSettings = "";
		StringVector lines;

		path p( SCOTT_CONFIGURATION_FILE );		

		if( exists(p) )
		{
			ifstream file( SCOTT_CONFIGURATION_FILE );

			string scottSettings = ReadAllFile(file);

			//replace_all( scottSettings, "\r\n", "\n");
			
			split( lines, scottSettings, is_any_of("\n") );		

			BOOST_FOREACH(string s, lines)
			{
				inputLines.push(s);
			}

			if ( !inputLines.empty() )
			{
				cout << "Execution of commands in the Scott Configuration file..." << endl << endl;
			}
		}


		for(;;)
		{
			// Read the user command
			string input = ReadLine();

			/************************************************************************/
			/*         Check if input is a plugin command invocation                */
			/************************************************************************/

			if ( ExecutePluginCommand(input) )
			{
				continue;
			}

			/************************************************************************/
			/*         Check if input is a shell command invocation                */
			/************************************************************************/

			if ( regex_search(input, m, regex("(\\w+)\\s*") ) )
			{
				string command = m[1];

				if ( IsShellCommand(command) )
				{
					ExecuteShellCommand(command, input);
					continue;
				}
			}

			// ...otherwise show a message error
			cout << endl << "Error: command not found." << endl << endl; 

		}	// for(;;)

	}
	catch(...)
	{
		cerr << "Critical error: scottsh will be closed.";
		cin.get();
	}

	// Exit from the shell
}

/***************************************************************/
/*                                                             */
/*             Utility methods implementation                  */
/*                                                             */
/***************************************************************/


void Shell::ExecuteShellCommand(string command, string input)
{
	// Command parsing and dispatch
	if ( command == "quit" || command == "exit" )	// quit and exit
	{
		exit( EXIT_SUCCESS );
	}
	else if ( input == "help" )		// help
	{
		Help();
	}
	else if ( regex_match( input, m, regex("help\\s+(.+)") ) )
	{
		Help( m[1] );
	}
	else if ( input == "plugins")   // plugins
	{
		Plugins();
	}
	else if ( regex_match( input, m, regex("load\\s+(.+)") ) )  // load
	{
		Load( m[1] ); 
	}	
	else if ( input == "commands")   // commands
	{
		Commands();
	}
	else if ( input == "types")   // types
	{
		Types();
	}
	else if ( input == "vars")	// vars
	{
		Vars();
	}		
	else if ( regex_match( input, m, regex("show\\s+(.+)") ) )   // show
	{
		Show( m[1] );
	}	
	else if ( regex_match( input, m, regex("var\\s+([^\\s^=]+)\\s+([^\\s^=]+)\\s*=\\s*(.+)") ) ) // var
	{
		Var( m[1], m[2], m[3] );
	}	
	else if ( regex_match( input, m, regex("autovars\\s+(.+)") ) )
	{
		Autovars( m[1] );
	}
	else if ( regex_match( input, m, regex("implicitvars\\s+(.+)") ) )
	{
		Implicitvars( m[1] );
	}
	else if ( regex_match( input, m, regex("dumpvars\\s+(.+)") ) )
	{
		Dumpvars( m[1] );
	}
	else if ( regex_match( input, m, regex("write\\s+\"([^\"]+)\"(.*)") ) ) 
	{
		Write( m[1], m[2], false );
	}	
	else if ( regex_match( input, m, regex("writeln\\s+\"([^\"]+)\"(.*)") ) ) 
	{
		Write( m[1], m[2], true );
	}	
	else if ( regex_match( input, m, regex("fopen\\s+(.+)") ) ) 
	{
		FOpen( m[1] );
	}	
	else if ( regex_match( input, m, regex("fwrite\\s+\"([^\"]+)\"(.*)") ) ) 
	{
		FWrite( m[1], m[2], false);
	}	
	else if ( regex_match( input, m, regex("fwriteln\\s+\"([^\"]+)\"(.*)") ) ) 
	{
		FWrite( m[1], m[2], true);
	}	
	else if ( regex_match( input, m, regex("fclose") ) ) 
	{
		FClose();
	}	
	else if ( input == "settings")	// vars
	{
		Settings();
	}		
	else
	{
		ShowShellCommandSyntax(command);
	}
}

bool Shell::IsShellCommand(string command)
{
	return command == "quit" ||
		command == "exit" ||
		command == "help" ||
		command == "load" ||
		command == "plugins" ||
		command == "commands" ||
		command == "types" ||
		command == "var" ||
		command == "show" ||
		command == "vars" ||
		command == "autovars" ||
		command == "implicitvars" ||
		command == "dumpvars" ||
		command == "write" ||
		command == "writeln" ||
		command == "fopen" ||
		command == "fwrite" ||
		command == "fwriteln" ||
		command == "fclose" ||
		command == "settings";
}

void Shell::ShowShellCommandSyntax(string command)
{
	cout << endl;
	cout << "Syntax: ";

	if ( command == "exit" || command == "quit")
	{
		cout << command << endl << endl;
		cout << "Exit the SCOTT shell";
	}
	else if ( command == "help" )
	{
		cout << "help [ 'shell command' | 'plugin command' | 'plugin type' ]" << endl << endl;
		cout << "Display helpful information about commands and types. " << endl <<
			    "If the argument is specified, gives detailed help on all commands or types matching argument, "
				"otherwise a list of the shell commands is printed.";
	}
	else if ( command == "plugins" )
	{
		cout << command << endl << endl;
		cout << "Show all the installed plugins.";
	}
	else if ( command == "load" )
	{
		cout << "load 'pluginName'" << endl << endl;
		cout << "Load a plugin and resolve all the dependences loading automatically all the dependent plugins. "
			    "The order of loading affect the process of name resolution. The shell use the principle of last-loaded-first.";
	}
	else if ( command == "commands" )
	{
		cout << command << endl << endl;
		cout << "Show all the available commands." << endl
			 << "Use the command 'load' to load a plugin.";
	}
	else if ( command == "types" )
	{
		cout << command << endl << endl;
		cout << "Show all the available custom types." << endl
 	 		 << "Use the command 'load' to load a plugin.";
	}
	else if ( command == "vars" )
	{
		cout << command << endl << endl;
		cout << "Show all the shell variables defined.";
	}
	else if ( command == "var" )
	{
		cout << "var 'type' 'varName' = 'expression'" << endl << endl;
		cout << "Create a new shell variable." << endl <<
			    "'type' can be a simple type or the name of a complex type." << endl <<
			    "'varName' is the name of the new variable" << endl <<
				"'expression' is the value to set the variable";
	}
	else if ( command == "autovars" )
	{
		cout << "autovars [ true | false ]" << endl << endl;
		cout << "Enable/Disable automatic output variables creation.";
	}
	else if ( command == "show" )
	{
		cout << "show 'expression'" << endl << endl;
		cout << "Show the value of an expression.";
	}
	else if ( command == "implicitvars" )
	{
		cout << "implicitvars [ true | false ]" << endl << endl;
		cout << "Enable/Disable implicit input variables mechanism.";
	}
	else if ( command == "dumpvars" )
	{
		cout << "dumpvars [ true | false ]" << endl << endl;
		cout << "If dumpvars is true a console output message is shown when a shell variable is automatically created.";
	}
	else if ( command == "write" )
	{
		cout << "write \"format\" [ expr1 expr2 ... ]" << endl << endl;
		cout << "Replaces each format item in the format string with the text equivalent of a corresponding expression's value and print all in the screen.";
	}
	else if ( command == "writeln" )
	{
		cout << "writeln \"format\" [ expr1 expr2 ... ]" << endl << endl;
		cout << "Replaces each format item in the format string with the text equivalent of a corresponding expression's value and print all in the screen plus a new line.";
	}
	else if ( command == "fopen" )
	{
		cout << "fopen 'file-name'" << endl << endl;
		cout << "Create and open a file to initiate an export session. Use shell commands 'fwrite' or 'fwriteln' to export variables and values from the shell."; 
	}
	else if ( command == "fwrite" )
	{
		cout << "fwrite \"format\" [ expr1 expr2 ... ]" << endl << endl;
		cout << "Replaces each format item in the format string with the text equivalent of a corresponding expression's value and write all in the file open with the command 'fopen'.";
	}
	else if ( command == "fwriteln" )
	{
		cout << "fwriteln \"format\" [ expr1 expr2 ... ]" << endl << endl;
		cout << "Replaces each format item in the format string with the text equivalent of a corresponding expression's value and write all, plus a new line, in the file opened with the command 'fopen'.";
	}
	else if ( command == "fclose" )
	{
		cout << "fclose" << endl << endl;
		cout << "Close the file open with the command 'fopen'. This close an export session."; 
	}
	else if ( command == "settings" )
	{
		cout << "settings" << endl << endl;
		cout << "Show the scott settings.";
	}

	cout << endl << endl;
}

bool Shell::IsPluginLoaded()
{
	if ( plugins->empty() )
	{
		cout << "Warning: before to use this command you need to load at least one plugin." << endl
			 << "Use the 'load' command to load a plugin." << endl;
		return false;
	}

	return true;
}

bool Shell::ExecutePluginCommand( string input )
{
	bool pluginCommandFound = false;
	string outputVarsString = "";
	string commandArgs = "";
	string command = "";

	// Check: [ v1, v2, ... ] = Command( par1, par2, ...)
	if ( regex_match(input, m, regex("\\[([^=]*)\\]\\s*=\\s*([^\\[\\]=]+)\\s*\\((.*)\\)") ) )
	{
		pluginCommandFound = true;

		outputVarsString = m[1];
		command = m[2];
		commandArgs = m[3];
	}
	else   // Check: v1 = Command( par1, par2, ...)
	if ( regex_match(input, m, regex("([^\\[\\]=]+)\\s*=\\s*([^\\[\\]=]+)\\s*\\((.*)\\)") ) )
	{
		pluginCommandFound = true;

		outputVarsString = m[1];
		command = m[2];
		commandArgs = m[3];
	}
	else   // Check: Command( par1, par2, ...)
	if ( regex_match(input, m, regex("([^\\[\\]=]+)\\s*\\((.*)\\)") ) )
	{
		pluginCommandFound = true;

		command = m[1];
		commandArgs = m[2];
	}

	if ( plugins->empty() || !pluginCommandFound)
	{
		return false;
	}

	// Command resolution
	try
	{
		command = nameResolutor->ResolveCommandName(command);
	}
	catch(const SArgumentException&)
	{
		cout << endl << "Error: there is not a plugin command with the name '" << command << "'." << endl << endl;
		return true;
	}

	/************************************************************************/
	/*    Input is a plugin command so the command will be executed         */
	/************************************************************************/

	trim(command);
	trim(commandArgs);

	try
	{
		// Create the fully qualified name from command

		// Get the command descriptor
		CommandDescriptorSp cd = descriptorsRepository->GetCommandDescriptor(command);

		CommandSp cmd;

		// Create the command using or not using implicit variables
		if ( implicitvars )
		{
			cmd = Command::Create(cd, commandArgs, vars, vars);					
		}
		else
		{
			cmd = Command::Create(cd, commandArgs, vars);
		}

		// If autovars is enabled and user don't create a custom output list
		// the output list is created automatically
		if ( autovars && outputVarsString.empty() )
		{
			ostringstream outputlist;

			for(int i = 0; i < cd->GetNumberOfOutputParameters(); i++)
			{
				OutputParameterSp par = cd->GetOutputParameter(i);

				// Create the output list
				if ( i > 0 ) outputlist << ", ";
				outputlist << par->GetName();
			}

			outputVarsString = outputlist.str();
		}

		// Get shell variables names from output list
		StringVector varsString;

		if ( ! outputVarsString.empty() )
		{
			boost::algorithm::split( varsString, outputVarsString, is_any_of(",") );		

			// validation and trimming of the output variables names
			for( int i=0; i < (int) varsString.size(); ++i )
			{
				string varName = varsString[i];
				trim(varName);

				if ( !Utility::IsValidName(varName) )
				{
					throw SException("Error: invalid name '" + varName + "' in the output arguments list.");
				}

				varsString[i] = varName;
			}
		}

		// Launch the command execution
		PluginSp plugin = (*plugins)[ cd->GetPluginName() ];
		try
		{
			plugin->ExecuteCommand( cmd );
		}
		catch(const runtime_error& ex)
		{
			throw SPluginException("Plugin error: " + string(ex.what()) );
		}
		catch(...)
		{
			throw SException("Internal error.");
		}

		// Create shell variables from outputs if specified
		if ( !outputVarsString.empty() )
		{
			SStructSp outputs = cmd->GetOutputs();

			int i = 0;
			while( i < cmd->GetNumberOfOutputs() && i < (int) varsString.size() )
			{
				// Get the variable name
				string varName = varsString.at(i);

				// Get the variable value from the command output
				SValueSp val = outputs->Get(i);

				// Set the shell variable
				if ( val )
				{
					(*vars)[varName] = val;

					// If dumpvars is true show the variable created
					if ( dumpvars )
					{
						 cout << val->GetTypeName() << " " << varName << " = ";
						 cout << Utility::GetPrettyPrintVar(val);
						 cout << endl;
					}
				}

				++i;
			}
		}
	}
	catch(const SException& ex)
	{
		cout << endl << endl << ex.Message() << endl << endl;
	}
	catch(const runtime_error& ex)
	{
		cout << endl << endl << ex.what() << endl << endl;
	}

	return true;
}

/***************************************************************/
/*                                                             */
/*        Scott Shell commands handlers implementation         */
/*                                                             */
/***************************************************************/

void Shell::Help()
{
	cout << endl << endl << "SCOTT Shell, Version 1.0" << endl;
	cout << "Type 'help name' to find out more about the command or type 'name'." << endl << endl;
	cout << "Available shell commands:" << endl << endl;
	cout << " - help [ 'shell command' | 'plugin command' | 'plugin type' ]" << endl;
	cout << " - plugins" << endl;
	cout << " - load 'pluginName'" << endl;
	cout << " - commands" << endl;
	cout << " - types" << endl;
	cout << " - vars" << endl;
	cout << " - show 'expression'" << endl;
	cout << " - var 'type' 'varName' = 'expression'" << endl;
	cout << " - autovars [ true | false ]" << endl;
	cout << " - implicitvars [ true | false ]" << endl;
	cout << " - dumpvars [ true | false]" << endl;
	cout << " - write \"format\" [ expr1 expr2 ... ]" << endl;
	cout << " - writeln \"format\" [ expr1 expr2 ... ]" << endl;
	cout << " - fopen 'file-name'" << endl;
	cout << " - fwrite \"format\" [ expr1 expr2 ... ]" << endl;
	cout << " - fwriteln \"format\" [ expr1 expr2 ... ]" << endl;
	cout << " - fclose" << endl;
	cout << " - settings" << endl;
	cout << " - [v1, v2, ...] = PluginCommand( expr1, expr2, ...)" << endl;
	cout << endl;
}

void Shell::Plugins()
{
	StringVectorSp installedPlugins = scott->GetInstalledPluginNames();

	cout << endl << "Installed plugins:" << endl;
	BOOST_FOREACH(string pluginName,  *installedPlugins)
	{
		cout << " - " << pluginName << endl;
	}

	cout << endl;
}

void Shell::Load(string pluginName)
{
	PluginSp plugin;

	// Check if the plugin is just loaded
	PluginSpMap::iterator it = plugins->find(pluginName);

	if ( it == plugins->end() )  // the plugin is not loaded
	{
		try
		{
			// Load the plugin
			plugin = scott->CreatePlugin(pluginName, plugins, nameResolutor);

			// Add the loaded plugin in the plugins map
			(*plugins)[pluginName] = plugin;
		}
		catch(const SException& ex)
		{
			cout << ex.Message() << endl;
			return;
		}
	}

	plugin = (*plugins)[pluginName];
	nameResolutor->AddOrSelectPlugin(plugin);
}

void Shell::Commands()
{
	if ( !IsPluginLoaded() )
	{
		return;
	}

	StringVectorSp commandNames = descriptorsRepository->GetCommandNames();

	cout << endl << "Available commands: " << endl;

	BOOST_FOREACH(string commandName, *commandNames )
	{
		cout << " - " << commandName << "()" << endl;
	}
	cout << endl;
}

void Shell::Help(string name)
{
	// Check if name is a shell command
	if ( IsShellCommand(name) )
	{
		ShowShellCommandSyntax(name);
		return;
	}
	
	// Check if name is a plugin command
	try
	{
		string commandName = nameResolutor->ResolveCommandName(name);
		CommandDescriptorSp cd = descriptorsRepository->GetCommandDescriptor(commandName);

		ostringstream outputlist;
		ostringstream inputlist;
		ostringstream usage;

		cout << endl << "Command: " << cd->GetFullyQualifiedName() << endl << endl;
		cout << "Description:" << endl;
		cout << cd->GetDescription() << endl << endl;
		cout << "Inputs:" << endl;
		
		for(int i = 0; i < cd->GetNumberOfInputParameters(); i++)
		{
			InputParameterSp par = cd->GetInputParameter(i);

			cout << " - " << par->GetFullyQualifiedTypeName() << 
				    " " << par->GetName() << " : " << par->GetDescription() << endl;

			// Create the input list
			if ( i > 0 ) inputlist << ", ";
			inputlist << par->GetName();
		}

		cout << endl << "Outputs:" << endl;
		
		for(int i = 0; i < cd->GetNumberOfOutputParameters(); i++)
		{
			OutputParameterSp par = cd->GetOutputParameter(i);

			cout << " - " << par->GetFullyQualifiedTypeName() << 
				    " " << par->GetName() << " : " << par->GetDescription() << endl;

			// Create the output list
			if ( i > 0 ) outputlist << ", ";
			outputlist << par->GetName();			
		}
		cout << endl;

		usage << "[ " << outputlist.str() << " ] = " << cd->GetName() << "( " << inputlist.str() << " )";

		cout << "Usage example: " << endl;
		cout << usage.str() << endl << endl;

		return;		
	}
	catch(const SException&) { }


	// Check if name is a plugin type
	
	try
	{
		string typeName = nameResolutor->ResolveTypeName(name);
		TypeDescriptorSp td = descriptorsRepository->GetTypeDescriptor(typeName);

		cout << endl << "Type: " << td->GetFullyQualifiedName() << endl << endl;
		cout << "Description:" << endl;
		cout << td->GetDescription() << endl << endl;

		if ( td->Describe(STypeEnum) )
		{
			cout << "Values: " << endl;

			SEnumTypeDescriptorSp etd = td->As<SEnumTypeDescriptor>();

			for(int i=0; i<etd->GetNumberOfEntries(); ++i)
			{
				SEnumEntrySp entry = etd->GetEntry(i);
				cout << " - " << entry->GetName() << ": " << entry->GetNumericValue() << endl;
			}
			cout << endl;
		}
		else if ( td->Describe(STypeVector) )
		{
			SVectorTypeDescriptorSp vectorTd = td->As<SVectorTypeDescriptor>();
			cout << "Item type: " << vectorTd->GetFullyQualifiedItemTypeName() << endl << endl;
		}
		else if ( td->Describe(STypeStruct) )
		{
			cout << "Members: " << endl;

			SStructTypeDescriptorSp structTd = td->As<SStructTypeDescriptor>();

			for(int i=0; i<structTd->GetNumberOfMembers(); ++i)
			{
				SStructMemberSp entry = structTd->GetMember(i);

				cout << " - " << entry->GetTypeName() << " " << entry->GetName() << " : " << entry->GetDescription() << endl;
			}
			cout << endl;
		}

		return;
	}
	catch(const SException&) { }

	// Show error message
	cout << "Error: '" << name << "' is not defined." << endl << endl;
}

void Shell::Types()
{
	if ( !IsPluginLoaded() )
	{
		return;
	}

	StringVectorSp types = descriptorsRepository->GetTypeNames();

	cout << endl << "Available types: " << endl;

	BOOST_FOREACH(string type, *types )
	{
		cout << " - " << type << endl;
	}

	cout << endl;
}

void Shell::Vars()
{
	cout << endl << "Shell variables: " << endl;

	BOOST_FOREACH( SValueSpMapPair elem, *vars)
	{
		SValueSp var = (*vars)[elem.first];

		cout << " - " << var->GetTypeName() << " " << elem.first << endl;
	}
	cout << endl;
}

void Shell::Show(string expression)
{
	try
	{
		SValueSp var = ExpressionResolutor::Resolve(expression, vars);

		cout << endl << "Type: " << var->GetTypeName() << endl;
		cout << "Value: ";

		cout << Utility::GetPrettyPrintVar(var);

		cout << endl << endl;
	}
	catch(const SException& ex)
	{
		cout << "Expression error: " << ex.Message() << endl << endl;
		return;
	}
}

void Shell::Var(string type, string varName, string expression)
{
	SValueSp var;
	trim(varName);

	try
	{
		SType stype = Utility::GetTypeFromName(type);

		try
		{
			var = ExpressionResolutor::Resolve(expression, vars, stype);
		}
		catch(...)
		{
			cout << "Expression parse error: '" + expression + "' is not a valid expression." << endl << endl;
			return;
		}

		if ( var->GetType() != stype )
		{
			cout << "Expression parse error: '" + expression + "' is not a '" << type << "' variable." << endl << endl;
			return;
		}

		(*vars)[varName] = var;
	}
	catch(...)  // type is a complex type
	{
		// If you create a complex variable must be loaded at least one plugin
		if ( !IsPluginLoaded() )
		{
			return;
		}

		try
		{
			string typeName = nameResolutor->ResolveTypeName(type);
			TypeDescriptorSp td = descriptorsRepository->GetTypeDescriptor(typeName);

			try
			{
				var = ExpressionResolutor::Resolve(expression, vars, td);

				if ( var->As<SComplexValue>()->GetTypeDescriptor() != td )
				{
					cout << "The expression type is not correct." << endl << endl;
					return;
				}
			}
			catch(...)
			{
				cout << "Expression parse error: '" + expression + "' is not a valid expression." << endl << endl;
				return;
			}

			(*vars)[varName] = var;
		}
		catch(...)
		{
			cout << "'" << type + "' is not defined in the loaded plugins." << endl << endl;
		}
	}
}

void Shell::Autovars( string val )
{
	autovars = (val == "true");
}

void Shell::Implicitvars( string val )
{
	implicitvars = (val == "true");
}

void Shell::Dumpvars( string val )
{
	dumpvars = ( val == "true" );
}

void Shell::Write(string format, string varList, bool endLine)
{
	try
	{
		cout << Utility::Format(format, varList, vars) << endl;
		if ( endLine ) cout << endl;
	}
	catch(const SException& ex)
	{
		cout << "Parse error: " << ex.Message() << endl;
	}
}

void Shell::FOpen(string fileName)
{
	try
	{
		// open the file
		exportFileStream.open( fileName.c_str() );
	}
	catch(...)
	{
		cout << "Error in opening or creating file." << endl;
	}
}

void Shell::FWrite(string format, string varList, bool endLine)
{
	if ( exportFileStream.is_open() )
	{
		try
		{
			exportFileStream << Utility::Format(format, varList, vars);
			if ( endLine ) exportFileStream << endl;
		}
		catch(const SException& ex)
		{
			cout << "Parse error: " << ex.Message() << endl;
		}
	}
	else
	{
		cout << "Error: you need to call 'fopen' before." << endl;
	}
}

void Shell::FClose()
{
	if ( exportFileStream.is_open() )
	{
		exportFileStream.close();
	}
}

void Shell::Settings()
{
	cout << endl << "Scott Settings:" << endl;

	cout << " - autovars: ";
	cout << (autovars ? "true" : "false") << endl;

	cout << " - implicitvars: ";
	cout << (implicitvars ? "true" : "false") << endl;

	cout << " - dumpvars: ";
	cout << (dumpvars ? "true" : "false") << endl;

	cout << endl;
}
